#include "UseOpenssl.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
 
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/ossl_typ.h>

using namespace std;

bool Base64EncoderbyOpenssl(char* inStr,int inLen,char** outStr,int &outLen)
{
	BIO *b64, *bio;
	BUF_MEM *bptr = NULL;
	size_t size = 0;

	if (inStr == NULL || outStr == NULL || *outStr != NULL)
		return false;

	b64 = BIO_new(BIO_f_base64());
	BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); //base64 编码是否存在换行问题

	bio = BIO_new(BIO_s_mem());
	bio = BIO_push(b64, bio);

	BIO_write(bio, inStr, inLen);

	if(BIO_flush(bio)) {
		BIO_get_mem_ptr(bio, &bptr);
		*outStr = new char[bptr->length+1];
		memcpy(*outStr, bptr->data, bptr->length);
		(*outStr)[bptr->length] = '\0';
		size = outLen = bptr->length;
	}

	BIO_free_all(bio);
	if(size > 0)
		return true;
	else
		return false;
}

void Base64EncoderFreebyOpenssl(char* str)
{
	if(str) {
		delete [] str;
		str = NULL;
	}
}

bool Base64DecoderbyOpenssl(char* inStr,int inLen,char* outStr,int &outLen)
{
	BIO *b64, *bio;
	//BUF_MEM *bptr = NULL;
	int size = 0;

	if (inStr == NULL || outStr == NULL || inLen <= 0)
		return false;

	b64 = BIO_new(BIO_f_base64());
	BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

	bio = BIO_new_mem_buf(inStr, inLen);
	bio = BIO_push(b64, bio);

	size = BIO_read(bio, outStr, inLen);
	outLen = size;

	BIO_free_all(bio);
	if(size <= 0) 
		return false;
	else
		return true;
}

bool GenerateNonceStringbyOpenssl(char* noncestr, int noncelen)
{
#define MAX_NONCE_STRING_LEN 128
	bool bsuc = false;
	if(noncestr && noncelen <= MAX_NONCE_STRING_LEN) {
		int i = 0;
		int seed_int[MAX_NONCE_STRING_LEN]={0};
		char noncesrc[MAX_NONCE_STRING_LEN+1] = {0};

		//生成随机数,srand初始化随机种子,rand产生随机数
		srand((unsigned)time(NULL));
		for (i = 0; i < noncelen; i++)
		{
			seed_int[i] = rand();
		}
		//生成伪随机数
		int bl = noncelen/2;
		RAND_seed(seed_int, noncelen);
		if(RAND_bytes((unsigned char*)noncesrc,bl))
		{
			for (i = 0; i < bl; i++)
			{
				sprintf(&noncestr[i*2],"%02x",noncesrc[i]);
			}
			noncestr[noncelen] = '\0';
			bsuc = true;
		}
		
	}

	return bsuc;
}

bool SHA1ByOpenssl(char* instr, int inlen)
{
#define SHA_DIGEST_LENGTHITMP 20
	if(instr == NULL || inlen < 0)
		return false;
	unsigned char digest[SHA_DIGEST_LENGTHITMP]; 
	char digeststring[SHA_DIGEST_LENGTHITMP*2+1] = {0};
	SHA1((unsigned char*)instr, inlen, (unsigned char*)digest);
	for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
	{
		sprintf(&digeststring[i*2], "%02x", (unsigned int)digest[i]);
	}
	digeststring[SHA_DIGEST_LENGTHITMP*2] = '\0';

	return true;
}

bool HMACSHAByOpenssl(const char * algo,  
					  const char * key, unsigned int key_length,  
					  const char * input, unsigned int input_length,  
					  unsigned char * output, unsigned int &output_length)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
	const EVP_MD * engine = NULL;  
	if(strcasecmp("sha512", algo) == 0) {  
		engine = EVP_sha512();  
	}  
	else if(strcasecmp("sha256", algo) == 0) {  
		engine = EVP_sha256();  
	}  
	else if(strcasecmp("sha1", algo) == 0) {  
		engine = EVP_sha1();  
	}  
	else if(strcasecmp("md5", algo) == 0) {  
		engine = EVP_md5();  
	}  
	else if(strcasecmp("sha224", algo) == 0) {  
		engine = EVP_sha224();  
	}  
	else if(strcasecmp("sha384", algo) == 0) {  
		engine = EVP_sha384();  
	}  
	else if(strcasecmp("sha", algo) == 0) {  
		engine = EVP_sha();  
	}  
	else if(strcasecmp("md2", algo) == 0) {  
		//engine = EVP_md2();  
	}  
	else {  
		//cout << "Algorithm " << algo << " is not supported by this program!" << endl;  
		return false;  
	}  

	//output = (unsigned char*)malloc(EVP_MAX_MD_SIZE);  

	HMAC_CTX ctx;  
	HMAC_CTX_init(&ctx);  
	HMAC_Init_ex(&ctx, key, strlen(key), engine, NULL);  
	HMAC_Update(&ctx, (unsigned char*)input, strlen(input)); 

	HMAC_Final(&ctx, output, &output_length);  
	HMAC_CTX_cleanup(&ctx);

#endif

	return true;  
}

bool AES_EncryptDecryptByOpenssl(int type,const char *key, const char *iv, char * strPlainText, int textLen ,char *outText, int & outlen,int bEncrypt)
{
	unsigned char outbuf[1024];
	int tmplen;
	bool bsuc = false;
	
	unsigned char hexkey[32] = {0};
	unsigned char hexiv[32] = {0};
	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
	EVP_CIPHER_CTX_init(ctx);

	do 
	{
		if(key == NULL || strPlainText == NULL || outText == NULL)
			break;

		if(32 < strlen(key))
			memcpy(hexkey,key,32);
		else
			strcpy((char*)hexkey,key);
		if (iv)
		{
			if(32 < strlen(iv))
				memcpy(hexiv,iv,32);
			else
				strcpy((char*)hexiv,iv);
		}


		if(e_aestype_ecb == type)
		{
			EVP_CipherInit_ex(ctx, EVP_aes_256_ecb(), NULL, hexkey, hexiv,bEncrypt);
		}
		else if (e_aestype_cbc == type)
		{
			EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), NULL, hexkey, hexiv,bEncrypt);
		}
		else if (e_aestype_ctr == type)
		{
			EVP_CipherInit_ex(ctx, EVP_aes_256_ctr(), NULL, hexkey, hexiv,bEncrypt);
		}
		else if (e_aestype_ofb == type)
		{
			EVP_CipherInit_ex(ctx, EVP_aes_256_ofb(), NULL, hexkey, hexiv,bEncrypt);
		}
		else if (e_aestype_cfb == type)
		{
			EVP_CipherInit_ex(ctx, EVP_aes_256_cfb(), NULL, hexkey, hexiv,bEncrypt);
		}
		else
		{
			break;
		}

		if(!EVP_CipherUpdate(ctx, outbuf, &outlen, (unsigned char*)strPlainText, textLen))
		{

			break;
		}

		if(!EVP_CipherFinal_ex(ctx, outbuf + outlen, &tmplen))
		{

			break;
		}
		outlen += tmplen;


		bsuc = true;
	} while (0);

	EVP_CIPHER_CTX_cleanup(ctx);
	EVP_CIPHER_CTX_free(ctx);

	if(bsuc)
		memcpy(outText,outbuf,outlen);
	

	return bsuc;
}
